library Buff requires /*
    */ TimerUtils /*
    */ Practicals /*
    */ 
    
    globals
        /*
        --------------------------------------------------------------------------
        # BUFF 极性
        --------------------------------------------------------------------------
        */
        constant integer BUFF_NONE = 0  
        constant integer BUFF_POSITIVE = 1    
        constant integer BUFF_NEGATIVE = 2
    endglobals

    /*
    --------------------------------------------------------------------------
    # 事件 API
    --------------------------------------------------------------------------
    */
    //! textmacro InsertRegisterBuffEvent takes NAME, VAR_NAME
    globals
        private CallBack array $VAR_NAME$List
        private integer $VAR_NAME$Size = 0
    endglobals

    function RegisterBuff$NAME$Response takes CallBack fun returns nothing
        set $VAR_NAME$List[$VAR_NAME$Size] = fun
        set $VAR_NAME$Size = $VAR_NAME$Size + 1
    endfunction

    private function EmitBuff$NAME$Event takes integer data returns nothing
        local integer i
        set i = 0
        loop
            exitwhen i >= $VAR_NAME$Size
            call $VAR_NAME$List[i].evaluate(data)
            set i = i + 1
        endloop
    endfunction
    //! endtextmacro

    // # RegisterBuffActiveResponse（Buff激活事件）
    //! runtextmacro InsertRegisterBuffEvent("Active", "buffEvent1")

    // # RegisterBuffApplyResponse（Buff应用事件）
    //! runtextmacro InsertRegisterBuffEvent("Apply", "buffEvent2")

    // # RegisterBuffRemoveResponse（Buff移除事件）
    //! runtextmacro InsertRegisterBuffEvent("Remove", "buffEvent3")

    // # RegisterBuffStackResponse（BUFF堆叠事件）
    //! runtextmacro InsertRegisterBuffEvent("Stack", "buffEvent4")


    struct Buff
        integer m_buffId
        //技能ID
        integer m_rawCode
        //BUFF 类型，增益减益
        integer m_buffKind

        //层级
        integer m_buffLevel
        //最大层级
        integer m_maxLevel
        //持续时间
        real m_duration
        //是否可驱散
        boolean m_enableDispel
        //是否可被盗取
        boolean m_enablePurloin

        integer m_data1
        integer m_data2
        integer m_data3
        integer m_data4

        real m_real1
        real m_real2
        real m_real3
        real m_real4

        effect m_effectA
        string m_effectAPath
        string m_effectAAttachName

        effect m_effectB
        string m_effectBPath
        string m_effectBAttachName
        
        effect m_effectC
        string m_effectCPath
        string m_effectCAttachName

        effect m_effectD
        string m_effectDPath
        string m_effectDAttachName

        timer m_timer
        unit m_unit

        private method reset takes nothing returns nothing
            set m_buffId = 0
            set m_rawCode = 0
            set m_unit = null

            set m_buffLevel = 1
            set m_maxLevel = 1
            set m_duration = 0.0
            set m_enableDispel = false
            set m_enablePurloin = false

            set m_data1 = 0
            set m_data2 = 0
            set m_data3 = 0
            set m_data4 = 0

            set m_real1 = 0.00
            set m_real2 = 0.00
            set m_real3 = 0.00
            set m_real4 = 0.00

            set m_effectA = null
            set m_effectAPath = null
            set m_effectAAttachName = null
            set m_effectB = null
            set m_effectBPath = null
            set m_effectBAttachName = null
            set m_effectC = null
            set m_effectCPath = null
            set m_effectCAttachName = null
            set m_effectD = null
            set m_effectDPath = null
            set m_effectDAttachName = null
        endmethod

        static method create takes integer buffId returns Buff
            local thistype this = thistype.allocate()
            call this.reset()
            set this.m_buffId = buffId
            return this
        endmethod

        private method applyLevel takes nothing returns nothing
            if m_buffLevel > 1 then
                //IncUnitAbilityLevel
                call SetUnitAbilityLevel(m_unit, m_rawCode, m_buffLevel)
            endif
        endmethod

        method addLevel takes integer level returns nothing
            if level > 0 then
                set m_buffLevel = m_buffLevel + level
                if m_buffLevel <= 0 then
                    set m_buffLevel = 1
                elseif m_buffLevel > m_maxLevel then
                    set m_buffLevel = m_maxLevel
                endif
                call this.applyLevel()
            endif
        endmethod

        method remove takes nothing returns nothing
            call EmitBuffRemoveEvent(this)

            call UnitRemoveAbility(this.m_unit, this.m_rawCode)
            call UnitRemoveAbility(this.m_unit, this.m_buffId)
            call RefreshUnitUI(this.m_unit)
            set this.m_unit = null

            call DestroyEffect(this.m_effectA)
            call DestroyEffect(this.m_effectB)
            call DestroyEffect(this.m_effectC)
            call DestroyEffect(this.m_effectD)
        endmethod

        method applyTo takes unit u returns nothing
            if this.m_unit != null then
                if m_unit != u then
                    call this.remove()
                else
                    //如果已有BUFF，则叠加一个等级
                    call this.addLevel(1)

                    call EmitBuffStackEvent(this)
                    return
                endif
            else
                call EmitBuffActiveEvent(this)
            endif

            set this.m_unit = u
            if GetUnitAbilityLevel(this.m_unit, m_rawCode) <= 0 then
                call UnitAddAbility(this.m_unit, m_rawCode)
                call UnitMakeAbilityPermanent(this.m_unit, true, m_rawCode)
                call this.applyLevel()

                call EmitBuffApplyEvent(this)
                debug call BJDebugMsg("Buff.applyTo " + GetUnitName(this.m_unit) + "; ABID: " + I2S(m_rawCode))
            endif
        endmethod

        method getTimer takes nothing returns timer
            if this.m_timer == null then
                set this.m_timer = NewTimer()
                call SetTimerData(this.m_timer, integer(this))
            endif
            return this.m_timer
        endmethod

        method destroy takes nothing returns nothing
            call ReleaseTimer(this.m_timer)
            set m_timer = null

            call this.remove()
            call this.reset()
            call this.deallocate()
        endmethod

    endstruct

    type BuffList extends Buff array [16]

endlibrary